/*
 * SqEngine 0.5.1
 * Date 28/08/2009 
 * by squid <squid@stormwind.it>
 * web: http://squareengine.codeplex.com
 * Basic Bot Component
 * Load PK3 models
 */

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using JigLibX.Collision;
using JigLibX.Physics;
using JigLibX.Geometry;
using JigLibX.Math;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using PK3Model;
using MD3Loader;
using sqengine.Components;
using sqengine.Engine.Common;
using SqEngine.Audio;

#endregion


namespace sqengine.PhysicObjects
{
    public abstract class BasicBotObject : Entity
    {
        #region Variables

        private Camera _camera;
        private Vector3 base_rotation = Vector3.Zero;

        protected CPK3Model model;
        protected WeaponInfo weaponinfo;
        protected Model weaponmodel;
        protected Texture2D weapontexture;
        protected Matrix weapon_mtx;
        protected SqAudio audioengine;


        protected Body body;
        protected CollisionSkin collision;
        private VertexPositionColor[] wf;
        protected Matrix[] boneTransforms = null;
        protected int boneCount = 0;

        #endregion

        #region Public Proprities
        public Body PhysicsBody { get { return body; } }
        public CollisionSkin PhysicsSkin { get { return collision; } }
        public Vector3 BaseRotation { get { return base_rotation; } set { base_rotation = value; } }

        public void SetModelAnimation(string animation) { model.setAnimation(animation); }

        #endregion

        public BasicBotObject(Game game, CPK3Model model, Vector3 baserotation, string start_animation, WeaponInfo WeaponModel)
            : base(game)
        {
            UpdateCamera();

            this.audioengine = ((sqengine)game).GetAudioEngine;

            this.model = model;
            this.weaponinfo = WeaponModel;
            this.base_rotation = baserotation;

            if (!string.IsNullOrEmpty(this.weaponinfo.WeaponModel))
                this.weaponmodel = Adcontent.LoadModel(this.weaponinfo.WeaponModel);

            if (!string.IsNullOrEmpty(this.weaponinfo.WeaponTexture))
                this.weapontexture = Adcontent.LoadTexture2D(this.weaponinfo.WeaponTexture);

            CRenderer.loadEffect(game.Content, "Effects/SimpleShader", game.GraphicsDevice);


            model.initialize(game.GraphicsDevice);
            model.useLighting = true;
            //Load default animation
            model.setAnimation("TORSO_STAND");
            model.setAnimation("LEGS_LANDB");


        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {

            model.update(gameTime);

            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            //Show collision box            
            DrawDebug((((sqengine)this.Game).DebugDrawer.Enabled));

            DrawPK3Model(gameTime);

            base.Draw(gameTime);
        }

        public abstract void ApplyEffects(BasicEffect effect);

        protected Vector3 SetMass(float mass)
        {
            PrimitiveProperties primitiveProperties =
                new PrimitiveProperties(PrimitiveProperties.MassDistributionEnum.Solid, PrimitiveProperties.MassTypeEnum.Density, mass);

            float junk; Vector3 com; Matrix it, itCoM;

            collision.GetMassProperties(primitiveProperties, out junk, out com, out it, out itCoM);
            body.BodyInertia = itCoM;
            body.Mass = junk;

            return com;
        }

        private void DrawDebug(bool state)
        {

            if (state)
            {
                if (collision != null)
                {
                    wf = collision.GetLocalSkinWireframe();

                    // if the collision skin was also added to the body
                    // we have to transform the skin wireframe to the body space
                    if (body.CollisionSkin != null)
                    {
                        body.TransformWireframe(wf);
                    }

                    ((sqengine)this.Game).DebugDrawer.DrawShape(wf);
                }

                CRenderer.renderAxis(this.CurrentGame.GraphicsDevice, this._camera.View, this._camera.Projection, weapon_mtx, 10.0f);
            }

        }

        private void UpdateCamera()
        {
            _camera = ((sqengine)this.Game).Camera;

        }

        private void DrawPK3Model(GameTime gameTime)
        {
            Matrix world = Matrix.Identity;
            Matrix m_rotation = Matrix.Identity;

            if (body.CollisionSkin != null)
            {

                world = Matrix.CreateScale(Scale) * GetMatrixRotation();
                //* Matrix.CreateRotationX(MathHelper.ToRadians(BaseRotation.X + Rotation.X));
                //* Matrix.CreateRotationY(MathHelper.ToRadians(BaseRotation.Y + Rotation.Y))
                //* Matrix.CreateRotationZ(MathHelper.ToRadians(BaseRotation.Z + Rotation.Z));

                world = world *
                    body.CollisionSkin.GetPrimitiveLocal(0).Transform.Orientation *
                    body.Orientation *   
                    Matrix.CreateTranslation(body.Position);

                m_rotation = world;
            }
            else
            {
                world = Matrix.CreateScale(Scale) *  GetMatrixRotation() * Matrix.CreateTranslation(body.Position);

            }

            model.render(this.Game.GraphicsDevice, world, this._camera.View, this._camera.Projection);

            weapon_mtx = model.getWeaponMatrix() * world;

            DrawWeapon(model.getWeaponMatrix(), world, this._camera.View, this._camera.Projection);


        }

        protected Matrix GetMatrixRotation(Matrix matrix)
        {

            matrix = matrix * Matrix.CreateRotationX(MathHelper.ToRadians(BaseRotation.X + Rotation.X))
                * Matrix.CreateRotationY(MathHelper.ToRadians(BaseRotation.Y + Rotation.Y))
                * Matrix.CreateRotationZ(MathHelper.ToRadians(BaseRotation.Z + Rotation.Z));

            return matrix;
        }
        protected Matrix GetMatrixRotation()
        {
            return GetMatrixRotation(Matrix.Identity);

        }

        private void DrawWeapon(Matrix WeaponMatrix, Matrix WorldMtx, Matrix ViewMtx, Matrix ProjMtx)
        {

            Vector3 weaponscale = new Vector3(this.weaponinfo.Weapon_x_off, this.weaponinfo.Weapon_y_off, this.weaponinfo.Weapon_x_off);

            if (this.weaponmodel != null)
            {
                foreach (ModelMesh m in this.weaponmodel.Meshes)
                {
                    foreach (BasicEffect e in m.Effects)
                    {
                        e.TextureEnabled = true;
                        e.Texture = weapontexture;
                        e.EnableDefaultLighting();

                        e.World = Matrix.CreateScale(8f) * Matrix.CreateRotationX((3.1415f / 2.0f) * 2.0f) *
                            Matrix.CreateRotationY(-3.1415f / 2f) *
                            WeaponMatrix * WorldMtx;

                        e.View = ViewMtx;
                        e.Projection = ProjMtx;
                    }

                    m.Draw();
                }
            }

        }


        protected BoundingBox GetBoundingBoxScaled(BoundingBox bndbox, Vector3 scale)
        {
            BoundingBox tmpbnd = bndbox;
            tmpbnd.Min = Vector3.Multiply(tmpbnd.Min, scale);
            tmpbnd.Max = Vector3.Multiply(bndbox.Max, scale);

            return tmpbnd;  

        }

        protected Box BuildPhysicsBoxFromBoundingBox(BoundingBox bound, Matrix matrix)
        {
            BoundingBox boundingBox = bound;
            Vector3 sides = new Vector3(boundingBox.Max.X - boundingBox.Min.X,
                boundingBox.Max.Y - boundingBox.Min.Y,
                boundingBox.Max.Z - boundingBox.Min.Z);
            return new Box(boundingBox.Min, matrix, sides);
        }

    }
}